/*	HTML5AS3MultiUp v0.1 <http://code.google.com/p/html5-as3-multiupload/> 
	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
*/

/*
使用示例: 
	
	* 更新, 因浏览器更新, 例子中的 fileName和fileSize 应改为 name和size

<!DOCTYPE html>
<html>
<head>
<title>HTML5 MULTI UPLOAD</title>
<meta charset='utf-8' />
</head>
<body onload="_init()">

<header>
<h1>HTML5多文件上传</h1> <a href="http://localhost/html5upload/upload.html">visit</a>
</header>

<section>

<ul id="filetree"></ul>
<p id="container"><!-- <input type="file" multiple /> --></p>
<p id="debug"></p>
<button id="subBtn">开始上传</button>

</section>

<script src="multiUpload.js"></script>
<script>
function _init(){
	var h5up = new HTML5MUP({
		container_id:	'container', 
		submit_id:	'subBtn', 
		url:			'upload.php',
		method:		'post',
		filter:			'*.jpg;*.jpeg;*.gif;*.png',
		max_filesize:	1*1024*1024,
		max_filecount:	5,
		callback_uploadstart: _onUploadStart,
		callback_listchange:	_onListChange,
		callback_progress: _onProgress,
		callback_error: _onError,
		callback_success: _onSuccess,
		callback_queueloaded: _onQueueLoaded,
		callback_notice: _onNotice
	});
	
	function _onUploadStart(fileList){
		var ul = document.getElementById('filetree'), 
			leng = fileList.length;
		
		for (var i=0;i<leng;i++)
			ul.getElementsByTagName('li')[i].removeChild(
				document.getElementById("fdel" + i)
			);
	}
	
	function _onListChange(fileList){
		var ul = document.getElementById('filetree'),
			leng = fileList.length,
			file = null;
		
		ul.innerHTML = '';
		
		for (var i=0;i<leng;i++){
			file = fileList[i];
			var del = document.createElement('a');
			del.href = 'javascript:void(0)';
			del.id = "fdel" + i;
			del.innerHTML = 'X';
			del.onclick = function(evt){
				var delIdx = (evt.srcElement||evt.target).id.replace('fdel','')*1;
				h5up.cancelItem( delIdx );
			};
			var li = document.createElement('li');
			li.name = 'file' + i;
			li.innerHTML = file.fileName + " - " + file.fileSize + " - " + (file.type.length?file.type:"unknown") + " - ";
			
			li.appendChild(del);
			ul.appendChild(li);
		}
	}
	
	function _onProgress(file, value){
		_debug( '正在上传: ' + file.fileName + ' ' + Math.round(value*100) + '%' );
	}
	
	function _onError(file, message){
		_debug( '发生错误:' + file.fileName + ' ' + message );
	}
	
	function _onSuccess(file, message){
		_debug('上传成功:' + file.fileName + ' ' + message);
	}
	
	function _onQueueLoaded(ids){
		_debug('队列完成:' + ids);
		alert('上传完成');
	}
	
	function _onNotice(message){
		_debug(message);
	}
	
	
	//---------------------------------------------------------------------------------------
	function _debug(str){
		document.getElementById('debug').innerHTML += '<br/>' + str;
	}
}
</script>

</body>
</html>
 */

/**
 * HTML5MUP
 * @param {} setting
 */
var HTML5MUP = (function(window, document){
	var Const = null,
		thisRef = null,
		_setting = null,
		_dom = null,
		_container = null,
		_submitBtn = null,
		_url = null,
		_uploadingFlag = -1,
		_serverRtnCache = [],
		_source = [],
		_boundary = '------html5mupboundary' + (new Date).getTime(),
		_hbrowser = (function(){
			var Brz = {},
				rwebkit = /(webkit)[ \/]([\w.]+)/,
				ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
				rmsie = /(msie) ([\w.]+)/,
				rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
				_ua = '',
				_uaMatch = function( ua ) {
					ua = ua.toLowerCase();
					_ua = ua;
					var match = rwebkit.exec( ua ) ||
						ropera.exec( ua ) ||
						rmsie.exec( ua ) ||
						ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
						[];
					return { browser: match[1] || "", version: match[2] || "0" };
				},
				browserMatch = _uaMatch( navigator.userAgent );
				
			if ( browserMatch.browser ) {
				Brz[ browserMatch.browser ] = true;
				Brz.version = browserMatch.version;
				Brz.ua = _ua;
			}
			if ( Brz.webkit ) {
				if (_ua.indexOf('safari')>-1 && _ua.indexOf('chrome')==-1)
					Brz.safari = true;
				else if (_ua.indexOf('chrome')>-1)
					Brz.chrome = true;
			}		
			return Brz;
		}()),
		_fixChromeSendBinary = function() {
			//http://code.google.com/p/chromium/issues/detail?id=35705
			if ( typeof XMLHttpRequest.prototype.sendAsBinary === 'undefined' ){
				XMLHttpRequest.prototype.sendAsBinary = function(datastr) {
					function byteValue(x) {
						return x.charCodeAt(0) & 0xff;
					}
					var ords = Array.prototype.map.call(datastr, byteValue);
					var ui8a = new Uint8Array(ords);
					this.send(ui8a.buffer);
				}
			}
		},
		_createInput = function(){
			var input = document.createElement('input');		
			input.type = 'file';
			input.name = 'upfiles';
			input.id = 'upfiles';
			input.setAttribute('multiple', 'multiple'); //ie6不认识这个
			_dom = input;
			if (input.multiple) input.name = 'upfiles[]';
			_container.appendChild(_dom);	
			//-- 在file控件中的选择或放弃动作
			_dom.onchange = function(){
				_onSelect.call(thisRef);	
			};	
			//-- 点击了上传按钮
			_submitBtn.onclick = function(){
				_dom.disabled = true;
				thisRef.upload();
			};	
			_submitBtn.disabled = true;
			
			if (_setting.dragbox_id)
			{
				var drag = document.getElementById(_setting.dragbox_id), 
					func = null;
				if (drag)
				{
					func = function(evt){
						_onDnd(evt);
					};
					drag.addEventListener('dragenter',func, false);
					drag.addEventListener('dragover',func, false);
					drag.addEventListener('dragout',func, false);
					drag.addEventListener('drop',func, false);
				}
				drag = null, func = null;
			}
		},
		_onDnd = function(evt){
			evt.stopPropagation();
			evt.preventDefault();

			if (evt.type == 'drop')
				_addFiles(evt.dataTransfer.files);
		},
		_onSelect = function(){
			_addFiles(_dom.files);
		},
		_addFiles = function(files){
			_submitBtn.disabled = false;
			_submitBtn.removeAttribute('disabled');

			var arr = [], sameArr = [];

			if (files && files.length)
				for (var ii=0; ii<files.length; ii++)
					arr.push( files[ii] );

			if (!_source.length)
			{
				_source = arr;
			}
			else
			{
				loop1: for (var ii = 0; ii < arr.length; ii++)
				{
					var newFile = arr[ii];

					loop2: for (var jj = 0; jj < _source.length; jj++)
					{
						var oldFile = _source[jj];

						if (newFile.name == oldFile.name
							&& newFile.size == oldFile.size)
						{
							sameArr.push(ii);
							continue loop1;
						}
					}
				}
				sameArr.sort(function(a,b){return b-a;});	//descending
				for (var m=0;m<sameArr.length;m++)
					arr.splice( sameArr[m], 1 );

				_source = _source.concat(arr);
			}

			_sliceSourceLength();
			_callback('listChange', _source);

			_dom.value = null;		
		},
		_queueUpload = function(){
			/*  http://www.w3.org/TR/FileAPI/
			File
				fileName: "bbs_login.rar"
				fileSize: 125186
				name: "bbs_login.rar"
				size: 125186
				type: "" | "image/jpeg" | "text/plain" | "text/html" | "application/vnd.ms-excel" ...
			*/
			var file	= _source[_uploadingFlag],
				xhr		= _getXMLHttpRequest(),
				rtn		= {id:0},
				thisRef	= this;
			
			//前端验证
			if ( !_isValidExtType(file.name) ){
				_callback('error',  file, _setting.i18n_err_type );
				
				_serverRtnCache.push(rtn);					
				_next();
				
				return;
			}else if ( file.size > _setting.max_filesize ){
				_callback('error',  file, 
					(_setting.i18n_err_size_pre + _setting.max_filesize/1024 + 'kb') );
				
				_serverRtnCache.push(rtn);					
				_next();
				
				return;
			}
			
			//TODO opera不支持 XMLHttpRequestUpload
			if (xhr.upload){
				xhr.upload.onprogress = function(evt){
					if (evt.lengthComputable)
						_callback('progress', file, evt.loaded / evt.total);
				};
			}
			
			xhr.onload = function(evt){
				//http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#dom-xmlhttprequest-readystate
				
				var eTgt = evt ? evt.target : xhr;	//Opera不认evt
				
				if (eTgt.status == 200){	//load
					
					//TODO 提取这个服务器逻辑, 使之通用
					rtn = _getJSON(xhr.responseText);
							
					if (rtn.resultCode*1 == 0)	//成功
					{
						_callback('success', file, /*rtn.describe*/ _setting.i18n_up_upload_ok);
					}
					else
					{
						_callback('error',  file, rtn.describe );
					}
					
					_serverRtnCache.push(rtn);					
					_next();
					
				}else if (eTgt.status == 404){ //error	
					//TODO 这里可能有更多错误情况
					
					_callback('error',  file,  _setting.i18n_up_ioerror );
						
					_serverRtnCache.push(rtn);					
					_next();
				}
			};
			
			//使Opera等不支持progress的浏览器也可以得到正在上传提示
			if ( typeof _getXMLHttpRequest().upload == 'undefined' )
				_callback('progress', _source[_uploadingFlag], .5);	
			
			xhr.open(
				_setting.method || 'post', 
				_setting.url, 
				true
			);
			
			if ( !_hbrowser.safari )	//ff chrome opera ie10 通用的rfc2388上传
			{
				var freader	= new FileReader;
				xhr.setRequestHeader('content-type', 'multipart/form-data; boundary=' + _boundary);
				
				//FF Chrome Opera 等
				if (typeof FileReader.prototype.readAsBinaryString != 'undefined')
				{
					//获得文件数据并上传
					freader.onload = (function(evt){ //chrome在 localhost下无法用onload
						xhr.sendAsBinary( _getUploadBinary(file, freader.result) );
					});
					freader.readAsBinaryString(file);
				}
				//IE10
				else
				{
					freader.onload = (function(evt){ //win8需要把上传文件夹的权限设为完全控制
						//http://www.khronos.org/registry/typedarray/specs/latest/
						var r = freader.result,
							u = new Uint8Array(r),
							s = '';
						
						for (var i=0; i< u.length; i++)
							s += String.fromCharCode( u[i] );
							
						xhr.sendAsBinary( _getUploadBinary(file, s) );
					});
					freader.readAsArrayBuffer(file);
				}
			}
			else						
			{
				//for safari 另一种简单的上传 但是server端不通用 http://valums.com/ajax-upload/
				//safari升级到5导致无法使用了 https://github.com/moxiecode/plupload/issues/320				
				xhr.setRequestHeader("Content-Type", "multipart/form-data");
				xhr.setRequestHeader("Cache-Control", "no-cache");
				xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
				xhr.setRequestHeader("X-File-Name", escape(file.name));
				xhr.setRequestHeader("X-File-Size", file.size);
				xhr.setRequestHeader("X-File-Type", file.type);
				xhr.send(file);
			}
		},
		_next = function(){
			if (++_uploadingFlag <= _source.length - 1) {
				//单个完成
				_queueUpload();
			}	else	{
				//队列完成
				_uploadingFlag = -1;
				
				_dom.disabled = false;
				_dom.removeAttribute('disabled');
				_submitBtn.disabled = true;
				
				if ("callback_queueloaded" in _setting){
					var ids = '', leng = _serverRtnCache.length, arr = [];
					for (var ii=0;ii<leng;ii++) arr.push(_serverRtnCache[ii].id);
					ids = arr.join(',');
					_callback('queueLoaded', ids);
				}
				
				if ("callback_reactive" in _setting){
					_callback('reactive');
				}
			}
		},
		_getXMLHttpRequest = function(){
			//目前其实只有ff和chrome可用, 其他有待浏览器更新后观察; ==> 更新: Opera已可用, 但速度很慢
			//http://www.w3.org/TR/XMLHttpRequest2/
			//http://www.w3.org/TR/progress-events/
			
			var xmlhttp=false;
			/*@cc_on @*/
			/*@if (@_jscript_version >= 5)
			 try {
			  xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
			 } catch (e) {
			  try {
			   xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
			  } catch (E) {
			   xmlhttp = false;
			  }
			 }
			@end @*/
			if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
				try {
					xmlhttp = new XMLHttpRequest();
				} catch (e) {
					xmlhttp=false;
				}
			}
			if (!xmlhttp && window.createRequest) {
				try {
					xmlhttp = window.createRequest();
				} catch (e) {
					xmlhttp=false;
				}
			}	
			return xmlhttp;
			
			//http://javascript.gakaa.com/operators--13-cc-1-on.aspx
			//http://www.jibbering.com/2002/4/httprequest.html
		},
		_getUploadBinary = function(file, data){
			// http://www.appelsiini.net/2009/10/html5-drag-and-drop-multiple-file-upload
			// http://www.himelon.com/2010/09/732.html
			var boundary = _boundary;
			var dashdash = '--';
			var crlf     = '\r\n';

			/* Build RFC2388 string. */
			var builder = '';

			builder += dashdash;
			builder += boundary;
			builder += crlf;
			
			/* Generate headers. */            
			builder += 'Content-Disposition: form-data; name="Filedata"';
			if (file.name) {
			  builder += '; filename="' + escape(file.name) + '"';
			}
			builder += crlf;

			builder += 'Content-Type: application/octet-stream';
			builder += crlf;
			builder += crlf; 

			/* Append binary data. */
			builder += data;
			builder += crlf;

			/* Write boundary. */
			builder += dashdash;
			builder += boundary;
			builder += crlf;
			
			/* Mark end of the request. */
			builder += dashdash;
			builder += boundary;
			builder += dashdash;
			builder += crlf;
			return builder;
		},
		_getJSON = function(str){
			return eval('(' + str + ')');
		},
		_isValidExtType = function(fileName){
			var ext = null, filter = null;
			try{
				ext = fileName.match(/\.([^\.]*)$/i)[1].toLowerCase();
				filter = _setting.filter.toLowerCase();
				return new RegExp( '\.'+ext+'\;?', 'i' ).test(filter);
			}catch(ex){}	
			return false;
		},
		_sliceSourceLength = function(){
			var max = ('max_filecount' in _setting)
							? _setting.max_filecount
							: 10;
							
			if ( _source.length > max ) {
				_source = _source.slice(0, max );
					
				_callback('notice', '保留选择队列中的前' + max + '个');
			}
		},
		_callback = function(){
			/*
			 * callback_uploadstart(fileList)
			 * callback_error(file, msg, idx)
			 * callback_notice(msg, idx)
			 * callback_listchange(fileList)
			 * callback_progress(file, value, idx)
			 * callback_success(file, msg, idx)
			 * callback_queueloaded(ids)
			 */	
			var args = [];	
			for (var i=0;i<arguments.length;i++) args[i] = arguments[i];	
			var type = args.shift();
			args.push(_uploadingFlag);

			if ("callback_" + type.toLowerCase() in _setting)	
				_setting["callback_" + type.toLowerCase()].apply(null, args);
		};
		
		
		
		
	Const = function(setting){
		_setting = setting;
		_container = document.getElementById(setting.container_id);
		_submitBtn = document.getElementById(setting.submit_id);
		_url = setting.url;
		
		thisRef = this;
		
		_fixChromeSendBinary();
		_createInput();
	};
	
	Const.browser = _hbrowser;
	Const.html5Enabled = function(){
		//safari升级到5导致无法使用了 https://github.com/moxiecode/plupload/issues/320
		var isSFR = _hbrowser.safari,
			isFF_CHR_OPR = ( _hbrowser.mozilla || _hbrowser.opera || _hbrowser.chrome ),
			isWIN8IE10 = ( _hbrowser.msie && _hbrowser.version>9 
				&& _hbrowser.ua.indexOf('windows nt 6.')>-1 && _hbrowser.ua.match(/windows\snt\s([\d\.]+)\;/)[1]*1>=6.2 );
		
		return (
			/* isSFR ||*/ 
				( 
					( isFF_CHR_OPR || isWIN8IE10 ) 
					&& (typeof window.FileReader != 'undefined') 
				)
		);
	};
	Const.dndEnabled = function(){
		return ('draggable' in document.createElement('span')) && !_hbrowser.safari;
	};
	
	Const.prototype = {
		/**
		 * 开始上传
		 */
		upload: function(){
			_uploadingFlag = 0;
			_serverRtnCache = [];
			//_source = [];
			
			_submitBtn.disabled = true;
			
			if (_source.length ) 
				_queueUpload();
			
			_callback('uploadStart', _source);
		},
		/**
		 * 放弃一个文件
		 * @param {} idx
		 */
		cancelItem: function(idx){
			if (_uploadingFlag != -1) return;	//正在上传
			_source.splice(idx, 1);
			_callback('listChange', _source);
		},
		/**
		 * 取得file控件
		 */
		getDom: function(){
			return _dom;
		},
		/**
		 * 回到初始态
		 */
		reactive: function(){
			_uploadingFlag = -1;
			_serverRtnCache = [];
			_source = [];
		}
	};
	
	return Const;
}(window, document));
 


//HTML5MUP.isBrowser = function(browserName){
	//return ( new RegExp('\\s' + browserName + '\\/', 'i')).test(window.navigator.userAgent);
//};